我们通过写一个从服务端(另外一个进程)获取用户名和密码作为demo来进行源码讲解。
- 先
new
一个IUserAidl.aidl
1 |
|
- make生成IUserAidl.java文件
写一个service作为服务端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Des:
* Created by zzw on 2018/1/26.
*/
public class MessageService extends Service {
public IBinder onBind(Intent intent) {
return new UserBinder();
}
private final class UserBinder extends IUserAidl.Stub {
public String getUserName() throws RemoteException {
return "zzw";
}
public String getPwd() throws RemoteException {
return "123456";
}
}
}AndroidManifest.xml注册service
1
2
3<service
android:process=":test"
android:name=".MessageService"/>Activity绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59public class MainActivity extends AppCompatActivity {
//客户端获取的aidl实例 , 通过这个实例就可以进行通讯
private IUserAidl mIUserAidl;
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
//连接
mIUserAidl = IUserAidl.Stub.asInterface(service);
Log.e("zzz", "连接成功");
}
public void onServiceDisconnected(ComponentName name) {
//断开连接
Log.e("zzz", "断开连接");
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//启动服务
Intent intent = new Intent(this, MessageService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
public void getUserName(View view) {
try {
if (mIUserAidl != null) {
String userName = mIUserAidl.getUserName();
Log.e("zzz", userName);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void getPwd(View view) {
try {
if (mIUserAidl != null) {
String pwd = mIUserAidl.getPwd();
Log.e("zzz", pwd);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
}
启动应用,发现是有test进程的 ,说明没问题。在当前客户端的进程(Acticity)可以发现是打印连接成功,然后依次点击获取用户名和密码,都对应打印成功,说明程序正常。我们接下来就开始分析源码,看看是怎么通讯的。
IUserAidl.java
1 | public interface IUserAidl extends android.os.IInterface { |
从上面这些代码我们可以知道,当客户端(Activity)和服务端(Service)bind的时候,服务端会把IUserAidl.Stub
当做IBinder
给传过来,然后通过IUserAidl.Stub.asInterface(service);
拿到真正的IUserAidl实现类IUserAidl.Stub.Proxy
。
我们进入IUserAidl.Stub.Proxy
这个类中,从调用的函数(getUserName getPwd)点进去查看是怎么进行通讯的。
1 | Proxy关键代码: |
看到实际是调用了IUserAidl.Stub
的transact
函数,但是在IUserAidl.Stub
中并没有transact
函数,只有一个onTransact
函数,这里我们可以猜测应该是调用transact
的时候调用了onTransact
函数
1 | Stub.onTransact关键代码 |
首先通过code
判断调用的是那个函数,然后在调用服务端的getUserName()
函数拿到对应的值,在写入reply
里面,然后在Proxy
里面通过_reply
读取出来,这样就完成了数据传递。
我们来验证我们的想法是不是在transact
调用了onTransact
。我们知道mRemote
是IUserAidl.Stub
,他是继承Binder
的,在Binder
查看到transact
函数
1 |
|
我们从这段代码就可以看到确实是在transact
中调用了onTransact
函数。
总结:
- 客户端通过
bind
拿到服务端IBinder
对象xxxx.Stub
,然后通过xxxx.Stub.asInterface
函数拿到对应的服务端通讯的代理类xxxx.Stub.Proxy
。 - 每个通讯的函数和都会生成一个
code
,当我们客户端调用函数时都会通过服务端xxxx.Stub
对象调用transact
函数,并将相应的code Parcel
对象传入,然后回调onTransact
函数,通过code
判断调用服务端的对应的函数,拿到对应的数据将之写入Parcel
里面 - 服务端调用完毕之后,客户端通过
Parcel
拿到对应的数据,然后返回即可。
v1.5.2